/**
 * 色块堆到顶端的时候，会和新的出块冲突，这个暂未处理，只实现了视频的效果。（网页版的会有条红线规避这个问题，但是竖条有时候也会撞）
 */


import { Control, control } from "./control";

const { ccclass, property } = cc._decorator;

const TIME_LIMIT = 15;
const MOVE_SPEED = 0.605;
const OVER_FLOW = 2;
const TOY_SIZE = 5;
const DESTROY_Y_ADJUST = -1085
const BLANK_REPAIR_LIMIT = 3;

const HSL_CONF = [
    [],
    [0.08, 0, 0], // 1
    [0, 0, 0],
    [0, 0, 0],
    [0.3, 0, 0], // 4
    [0.7, 0, 0],
    [0.5, 0.1, 0],
    [0.01, 0.15, 0],  // 7
]

@ccclass
export default class View extends cc.Component {

    @property(cc.Node)
    layout_node: cc.Node = null;

    @property(cc.Node)
    game_over_node: cc.Node = null;

    @property(cc.Node)
    shop_node: cc.Node = null;

    @property(cc.Node)
    birth_node: cc.Node = null;

    @property(cc.Node)
    touch_node: cc.Node = null;

    @property(cc.Node)
    guide_node: cc.Node = null;

    @property(cc.Prefab)
    block_pfb: cc.Prefab = null;

    @property(cc.Prefab)
    destroy_pfb: cc.Prefab = null;

    toy_node: cc.Node;

    control: Control;

    move_distance: number;
    now_rows: number;
    cols: number;
    block_map: cc.Node[][];

    toy_index: number; // toy序列
    destroy_color_index: number;

    timer: number; // 计时15秒
    last_destroy_i: number; // 销毁砖块中最下的一层
    bingo_lines: number; // 一次销毁了几行

    blank_repair_times: number; // 不太理解具体的业务需求，从视频12秒看好像是联销三次之后不会填补空位向下落

    _left_limit: number;
    _right_limit: number;

    touchListening: boolean;

    onLoad() {
        this.control = control;
        this.timer = -1;
        this.move_distance = 0;
        this.now_rows = 0;
        this.toy_index = 0;
        this.blank_repair_times = 0;
        this.cols = this.control.getCols()
        this.initBlocks();
        this.initGameOver();
        this.genToy();
        this.listenTouch();
    }


    get block_size() {
        return this.control.getTileSize()
    }

    get left_limit() {
        return this._left_limit;
    }

    set left_limit(val: number) {
        this._left_limit = val;
    }

    get right_limit() {
        return this._right_limit;
    }

    set right_limit(val: number) {
        this._right_limit = val;
    }

    start() {
    }

    listenTouch() {
        if (this.touchListening) return;
        this.touchListening = true;
        // let toy_node = this.toy_node;
        if (cc.isValid(this.touch_node)) {
            this.touch_node.on(cc.Node.EventType.TOUCH_MOVE, (event) => {
                if (cc.isValid(this.toy_node)) {
                    let loc_x = event.getLocationX()
                    let move_x = this.left_limit > loc_x ? this.left_limit : (this.right_limit < loc_x ? this.right_limit : loc_x);
                    this.toy_node.x = move_x - this.block_size * this.cols / 2 - this.block_size + (5 - this.toy_node["init_x"]) * this.block_size;
                }
            }, this)
            this.touch_node.on(cc.Node.EventType.TOUCH_END, (event) => {
                if (cc.isValid(this.guide_node)) {
                    this.guide_node.active = false;
                    this.timer = 0;
                }
                // 下落
                this.dropToy(event.getLocationX());
            }, this)
        }
    }

    cancelListenTouch() {
        if (!this.touchListening) return;
        this.touchListening = false;
        if (cc.isValid(this.touch_node)) {
            this.touch_node.off(cc.Node.EventType.TOUCH_MOVE);
            this.touch_node.off(cc.Node.EventType.TOUCH_END);
        }
    }

    // 初始化gameover界面
    initGameOver() {
        if (cc.isValid(this.game_over_node)) {
            this.game_over_node.getChildByName("btn_mask").on("click", this.onClickGameOver, this);
            this.game_over_node.getChildByName("btn").on("click", this.onClickGameOver, this);
        }
    }

    // 点击gameover界面进入商店
    onClickGameOver() {
        if (cc.isValid(this.shop_node)) {
            console.log("clickGameOver");
            this.shop_node.active = true;
        }
    }

    // 初始化阵列
    initBlocks() {
        this.block_map = Array.apply(null, { length: this.control.getRows() }).map(() => {
            return new Array(this.cols);
        })

        for (let i = 0; i < this.control.getDisplayRows() + OVER_FLOW; i++) {
            this.addRows(i);
        }
        this.move_distance = this.now_rows * this.block_size;
    }

    // 从底部添加一行砖块
    addRows(row: number) {
        var self = this;
        for (let j = 0; j < this.control.getRows(); j++) {
            let block_num = this.control.getBlockNum(row, j);
            if (block_num) {
                if (cc.isValid(self.layout_node)) {
                    let block = cc.instantiate(this.block_pfb);
                    block["block_num"] = block_num
                    if (!self.block_map[row]) {
                        self.block_map[row] = [];
                    }
                    self.block_map[row][j] = block;
                    self.configBlocks(row, j, self);
                }
            }
        }
        this.now_rows = row;
    }

    // 设置砖块颜色和位置等属性
    configBlocks(row: number, col: number, self: typeof this) {
        self = self || this;
        let block = self.block_map[row][col];
        if (cc.isValid(block)) {
            let block_size = self.block_size;
            block.x = block_size * col - block_size * this.cols / 2;
            block.y = - block_size * row - block_size;
            let block_num = block["block_num"];
            self.control.setBlockColor(block, block_num, (err, spf) => {
                self.layout_node.addChild(block);
            })
        }
    }

    // 检查是否gameover
    checkGameOver() {
        // 根据移动距离和顶部空间计算出是否超框
        let empty_rows = 0;
        for (let i = 0; i < this.block_map.length; i++) {
            const block_row = this.block_map[i];
            for (let j = 0; j < block_row.length; j++) {
                const block = block_row[j];
                if (cc.isValid(block)) {
                    empty_rows = i;
                    if (empty_rows < this.now_rows - this.control.getDisplayRows() - 1) {
                        this.gameOver();
                        return true;;
                    }
                }
            }
        }
    }

    // 激活gameover界面，停止计时
    gameOver() {
        if (cc.isValid(this.game_over_node)) {
            this.game_over_node.active = true;
            this.timer = -2;
        }
        this.cancelListenTouch();
    }

    // 下一个toy
    genToy(): cc.Node {
        if (this.checkGameOver()) {
            return;
        };
        this.listenTouch();
        let toy_conf = this.control.getToyConf(this.toy_index);
        let x = toy_conf.x;
        let toy_shape_conf = this.control.getToyShape(toy_conf.type);
        let toy_color = toy_conf.color;
        this.destroy_color_index = toy_color;
        let self = this;
        let toy_node = new cc.Node("toy_node");
        this.toy_node = toy_node;
        toy_node["shape"] = toy_shape_conf;
        toy_node["init_x"] = x;
        this.birth_node.removeAllChildren();
        toy_node.parent = this.birth_node;
        for (let i = 0; i < TOY_SIZE; i++) {
            for (let j = 0; j < TOY_SIZE; j++) {
                let val = toy_shape_conf[i][j];
                if (val === 1) {
                    if (cc.isValid(self.birth_node)) {
                        let block = cc.instantiate(this.block_pfb);
                        block.parent = toy_node;
                        block.x = (x + j) * self.block_size - self.block_size * self.cols / 2;
                        block.y = self.block_size * (TOY_SIZE - i - 1);
                        self.control.setBlockColor(block, toy_color);
                    }
                }
            }
        }

        // console.log(this.toy_node.children)
        // 分析x轴可移动的最大区间，这里暂时只计算墙体边缘，不计算其他已上升的block
        let left_limit = this.block_size / 2;
        let right_limit = this.cols * this.block_size - this.block_size;
        for (let i = 0; i < TOY_SIZE; i++) {
            let mid = TOY_SIZE >> 1
            for (let j = 1; j <= mid; j++) {
                if (toy_shape_conf[j][mid - i] === 1) {
                    left_limit = this.block_size / 2 + i * this.block_size;
                }
                if (toy_shape_conf[j][mid + i] === 1) {
                    right_limit = this.cols * this.block_size - this.block_size * (1.5 + i);
                }
            }
        }
        // console.log("limit: ", left_limit, right_limit)

        this.left_limit = left_limit;
        this.right_limit = right_limit;

        cc.tween(toy_node)
            .to(0.3, { position: cc.v3(0, - this.block_size * TOY_SIZE, 0) }, { easing: "quadIn" })
            .call(() => {
            })
            .start();
        return toy_node;
    }

    dropToy(drop_x: number) {
        this.blank_repair_times = 0;
        let toy_node = this.toy_node;
        // 更改父节点
        toy_node.parent = this.layout_node;
        toy_node.y = toy_node.y - this.layout_node.y + cc.winSize.height / 2;
        // 计算水平补正
        let col_idx = Math.floor(drop_x / this.block_size);
        let target_x = this.block_size * col_idx - this.block_size * this.cols / 2 - this.block_size / 2 + (5 - this.toy_node["init_x"]) * this.block_size;

        // 计算下坠距离
        let shape = toy_node["shape"];
        // [0, 0, 0, 0, 0],
        // [0, 0, 0, 0, 0],
        // [0, 1, 1, 0, 0],
        // [0, 0, 1, 0, 0],
        // [0, 0, 1, 0, 0]
        let target_y = -Infinity;
        for (let i = 0; i < TOY_SIZE; i++) {
            let step = 0;
            for (let j = 0; j < TOY_SIZE; j++) {
                if (shape[j][i] === 1) {
                    step = j;
                }
            }
            if (step > 0) {
                // 计算一个距离
                let y_1 = toy_node.y + (TOY_SIZE * 2 - 1 - step) * this.block_size + this.move_distance - 1320;
                // 对应的col
                let d_col = col_idx + i - (TOY_SIZE >> 1);
                // 检查这一列第一个block
                // console.log("y: ", y_1, i, step, toy_node.y, this.move_distance - 1320);
                for (let i = - Math.floor((toy_node.y + y_1) / this.block_size); i < this.block_map.length; i++) {
                    if (cc.isValid(this.block_map[i][d_col])) {
                        // this.block_map[i][d_col].scale = 0.5
                        let y_top = this.block_map[i][d_col].y + this.block_size;
                        if (target_y < y_top - y_1) {
                            target_y = y_top - y_1;
                        }
                        break;
                    }
                }
            }
        }

        // 下坠
        let self = this;
        cc.tween(toy_node)
            .to(0.2, { position: cc.v3(target_x, target_y, 0) }, { easing: "quadIn" })
            .call(() => {
                for (let i = 0; i < TOY_SIZE; i++) {
                    for (let j = 0; j < TOY_SIZE; j++) {
                        if (shape[i][j] && cc.isValid(self)) {
                            // console.log(toy_node.children)
                            // console.log(shape)
                            // console.log(shape[i][j], i, j)
                            let toy_unit = toy_node.children[0];
                            toy_unit.parent = self.layout_node;
                            toy_unit.y = toy_unit.y + toy_node.y;
                            toy_unit.x = toy_unit.x + toy_node.x;
                            // console.log("---: ", target_y, - target_y / self.block_size - TOY_SIZE + i, col_idx - (TOY_SIZE >> 1) + j)
                            self.block_map[- target_y / self.block_size - TOY_SIZE + i][col_idx - (TOY_SIZE >> 1) + j] = toy_unit;
                        }
                    }
                }
                self.toy_index++;
                self.checkBingo();
                // self.genToy();
            })
            .start()
    }
    
    checkBingo() {
        this.cancelListenTouch();
        // todo: 可以记录当前行数进度来减少遍历
        if (!this.block_map || !this.block_map.length || !this.block_map[0].length) return;
        let bingo_arr: number[] = [];
        for (let i = 0; i < this.block_map.length; i++) {
            let has_bingo = true;
            for (let j = 0; j < this.block_map[0].length; j++) {
                if (cc.isValid(this.block_map[i][j])) {
                    // this.block_map[i][j].scale = 0.5;
                    this.block_map[i][j].getChildByName("i").getComponent(cc.Label).string = String(i);
                    this.block_map[i][j].getChildByName("j").getComponent(cc.Label).string = String(j);
                }
                if (!cc.isValid(this.block_map[i][j])) {
                    has_bingo = false;
                    break;
                }
            }
            if (has_bingo) {
                bingo_arr.push(i);
            }
        }
        if (!bingo_arr.length) {
            this.genToy();
            return;
        }
        for (let i = 0; i < bingo_arr.length; i ++) {
            let destroy_y = 0;
            for (let j = 0; j < this.block_map[0].length; j ++) {
                let destroy_node = this.block_map[bingo_arr[i]][j]
                if (cc.isValid(destroy_node)) {
                    destroy_y = destroy_node.y;
                    destroy_node.removeFromParent();
                    this.block_map[bingo_arr[i]][j] = null;
                }
            }
            let destroy_ani_node = cc.instantiate(this.destroy_pfb);
            let mtl = destroy_ani_node.getComponent(cc.Sprite).getMaterial(0);
            let hsl = HSL_CONF[this.destroy_color_index || 1]
            mtl.setProperty("h", hsl[0]);
            mtl.setProperty("s", hsl[1]);
            // mtl.setProperty("l", hsl[2]);
            destroy_ani_node.parent = this.layout_node;
            destroy_ani_node.y = destroy_y + DESTROY_Y_ADJUST;
            if (i === bingo_arr.length - 1) {
                this.last_destroy_i = bingo_arr[i];
                this.bingo_lines = bingo_arr.length;
                destroy_ani_node.getComponent(cc.Animation).on("finished", this.checkRowFall, this);
            }
            this.scheduleOnce(() => {
                destroy_ani_node.removeFromParent();
            }, 55/60)
        }
    }

    checkRowFall() {
        if (!cc.isValid(this) || !this.last_destroy_i) return;

        let under_blank_arr: number[] = Array.apply(null, {length: this.block_map[0].length}).map(() => {return 0});
        this.blank_repair_times ++;
        if (this.blank_repair_times < BLANK_REPAIR_LIMIT) {
            for (let j = 0; j < this.block_map[0].length; j ++) {
                for (let i = this.last_destroy_i + 1; i < this.block_map.length; i ++) {
                    if (!cc.isValid(this.block_map[i][j])) {
                        under_blank_arr[j] ++;
                    } else {
                        break;
                    }
                }
            }
        } else {
            this.blank_repair_times = 0;
        }
        let delay = 0.3;
        for (let j = 0; j < this.block_map[0].length; j ++) {
            let blanks = under_blank_arr[j];
            for (let i = this.last_destroy_i; i >= 0; i --) {
                let block = this.block_map[i][j]
                if (cc.isValid(block)) {
                    if (blanks > this.bingo_lines) {
                        // delay = 0.6;
                        this.destroy_color_index = block["block_num"] || 1;
                    }
                    // 调整表中顺序
                    this.block_map[i + blanks][j] = block;
                    this.block_map[i][j] = null;
                    cc.tween(block)
                        // .to(blanks > this.bingo_lines ? 0.6 : 0.3, {y: block.y - this.block_size * blanks})
                        .to(0.3, {y: block.y - this.block_size * blanks})
                        .start();
                } else {
                    blanks ++;
                }
            }
        }

        this.scheduleOnce(() => {
            this.checkBingo();
        }, delay + 1 / 30);
        
    }

    update(dt) {
        if (this.timer >= 0) {
            this.timer += dt;
            if (this.timer < TIME_LIMIT) {
                let speed = MOVE_SPEED;
                if (cc.isValid(this.layout_node)) {
                    this.layout_node.y += speed;
                    this.move_distance += speed;
                    let move_rows = this.move_distance / this.block_size;
                    if (move_rows > this.now_rows) {
                        // 加载下一行
                        this.addRows(this.now_rows + 1);
                        this.checkGameOver();
                    }
                }
            } else {
                this.gameOver();
            }
        }
    }

}
